﻿
#region Usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading.Tasks;
using System.Collections;
using System.Threading;
using System.Windows.Threading;
using System.Windows.Controls.Primitives;
using System.IO;
using System.Windows.Markup;
using System.Reactive;
using System.Reactive.Linq;
using System.Collections.ObjectModel;
#endregion

namespace FasterWPF
{
    public static class ListBoxExt
    {
        #region Add Alternating Row Colors

        public static SettingsChain<ListBox> SetAlternatingRows(this SettingsChain<ListBox> chain, Brush rowColor1, Brush rowColor2)
        {
            ((ListBox)chain.Item1).AddAlternatingRows(rowColor1, rowColor2);

            return chain;
        }

        public static void AddAlternatingRows(this ListBox chain, Brush rowColor1, Brush rowColor2)
        {
            ListBox lbx = chain;// ((ListBox)((ContentControl)((StackPanel)p.Parent).Parent).Tag);
            ((ItemsControl)lbx).AlternationCount = 2;

            Style style = new Style(typeof(ListBoxItem), (Style)lbx.Resources[typeof(ListBoxItem)]);

            Trigger t1 = new Trigger();
            t1.Property = ItemsControl.AlternationIndexProperty;
            t1.Value = 0;

            Setter setter1 = new Setter();
            setter1.Property = Control.BackgroundProperty;
            setter1.Value = rowColor1;

            Trigger t2 = new Trigger();
            t2.Property = ItemsControl.AlternationIndexProperty;
            t2.Value = 1;

            Setter setter2 = new Setter();
            setter2.Property = Control.BackgroundProperty;
            setter2.Value = rowColor2;

            t1.Setters.Add(setter1);
            style.Triggers.Add(t1);

            t2.Setters.Add(setter2);
            style.Triggers.Add(t2);

            lbx.Resources[typeof(ListBoxItem)] = style;
        }

        #endregion

        #region Core ListBox-Specific Construction Methods

        public static ListBox Initialize(this ListBox source, ContainerType containerType)
        {
            return Initialize(source, double.NaN, double.NaN, containerType); //Default width and height to Auto by setting to NaN
        }

        public static ListBox Initialize(this ListBox source, ContainerType containerType, int? rowSpan, int? colSpan)
        {
            if (rowSpan != null)
            {
                Grid.SetRowSpan(source, (int)rowSpan);
            }

            if (colSpan != null)
            {
                Grid.SetColumnSpan(source, (int)colSpan);
            }

            return Initialize(source, double.NaN, double.NaN, containerType); //Default width and height to Auto by setting to NaN
        }

        public static ListBox Initialize(this ListBox source, double width, double height, ContainerType containerType, int? rowSpan, int? colSpan)
        {
            if (rowSpan != null)
            {
                Grid.SetRowSpan(source, (int)rowSpan);
            }

            if (colSpan != null)
            {
                Grid.SetColumnSpan(source, (int)colSpan);
            }

            return Initialize(source, width, height, containerType); //Default width and height to Auto by setting to NaN
        }

        public static ListBox Initialize(this ListBox source, double width, double height, ContainerType containerType)
        {
            //Force the Aero theme by default to assure the structure of the Listbox
            Uri uri = new Uri("/PresentationFramework.Aero;component/themes/Aero.NormalColor.xaml", UriKind.Relative);
            source.Resources.MergedDictionaries.Add(Application.LoadComponent(uri) as ResourceDictionary);

            source.Width = width;
            source.Height = height;
            source.SetContainerType(containerType);
            IComposite icomp = CompositeTypeHelper.CreateIComposite<ListBox>(source);
            source.SetIComposite<ListBox>(icomp);

            //STORE SETTING BUILD CHAIN AND GET CHAIN?!?  Put these in a Composite implements an interface
            ListBoxParent lbsr = new ListBoxParent();
            source.StoreSetting<ListBox>("IParent", lbsr);
            source.StoreSetting<ListBox>("IScrollViewerStrategy", lbsr);

            source.AddItemBorder(Brushes.Gray, new Thickness(0, 0, 0, 1)); //default Border     
            source.StoreSetting<ListBox>("BorderSettings", new BorderSettings(){ Width = double.NaN, Height = double.NaN, BorderBrush = Brushes.Gray, BorderThickness= new Thickness(0, 0, 0, 0), CornerRadius = new CornerRadius(0), Padding = new Thickness(0) } );

            setListViewItemPaddingToZero(source);

            ScrollViewer closeOverViewer = source.GetScrollViewer();
            closeOverViewer.CanContentScroll = false;  //Logical scrolling seems buggy. Therefore, always force physical scrolling
            
            //source.StoreSetting<ListBox>("ScrollViewerDeltaStep", 1);
            //source.StoreSetting<ListBox>("HorizScrollViewerDeltaStep", 1);

            //source.StoreSetting<ListBox>("ScrollViewerScrollDownStep", 1);
            //source.StoreSetting<ListBox>("ScrollViewerScrollDownDelay", 1000);
            //source.StoreSetting<ListBox>("ScrollViewerScrollDownInterval", 100);
            //source.StoreSetting<ListBox>("ScrollViewerPageDownStep", 1);
            //source.StoreSetting<ListBox>("ScrollViewerPageDownDelay", 1000);
            //source.StoreSetting<ListBox>("ScrollViewerPageDownInterval", 100);

            //source.StoreSetting<ListBox>("ScrollViewerPageUpStep", 1);
            //source.StoreSetting<ListBox>("ScrollViewerPageUpDelay", 1000);
            //source.StoreSetting<ListBox>("ScrollViewerPageUpInterval", 100);
            //source.StoreSetting<ListBox>("ScrollViewerScrollUpStep", 1);
            //source.StoreSetting<ListBox>("ScrollViewerScrollUpDelay", 1000);
            //source.StoreSetting<ListBox>("ScrollViewerScrollUpInterval", 100);

            //source.StoreSetting<ListBox>("ScrollViewerScrollRightStep", 1);
            //source.StoreSetting<ListBox>("ScrollViewerScrollRightDelay", 1000);
            //source.StoreSetting<ListBox>("ScrollViewerScrollRightInterval", 100);
            //source.StoreSetting<ListBox>("ScrollViewerPageRightStep", 1);
            //source.StoreSetting<ListBox>("ScrollViewerPageRightDelay", 1000);
            //source.StoreSetting<ListBox>("ScrollViewerPageRightInterval", 100);

            //source.StoreSetting<ListBox>("ScrollViewerPageLeftStep", 1);
            //source.StoreSetting<ListBox>("ScrollViewerPageLeftDelay", 1000);
            //source.StoreSetting<ListBox>("ScrollViewerPageLeftInterval", 100);
            //source.StoreSetting<ListBox>("ScrollViewerScrollLeftStep", 1);
            //source.StoreSetting<ListBox>("ScrollViewerScrollLeftDelay", 1000);
            //source.StoreSetting<ListBox>("ScrollViewerScrollLeftInterval", 100);

            source.StoreSetting<ListBox>("ObsCol", new ObservableCollection<Border>());

            //ScrollViewerExt.SetVerticalScrollButtonDownHandler(source);
            //ScrollViewerExt.SetVerticalPageButtonDownHandler(source);
            //ScrollViewerExt.SetHorizontalScrollButtonRightHandler(source);
            //ScrollViewerExt.SetHorizontalPageButtonRightHandler(source);

            //ScrollViewerExt.SetVerticalPageButtonUpHandler(source);
            //ScrollViewerExt.SetHorizontalScrollButtonLeftHandler(source);
            //ScrollViewerExt.SetHorizontalPageButtonLeftHandler(source);

            //ScrollViewerExt.SetThumbDragDeltaHandler(source, lbsr);
            //ScrollViewerExt.SetHorizThumbDragDeltaHandler(source, lbsr);

            return source;
        }

        public static void UseWrapPanel(this ListBox source)
        {
            source.ItemsPanel = new ItemsPanelTemplate(new FrameworkElementFactory(typeof(WrapPanel)));
            source.GetScrollViewer().HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
        }

        public static void UseHorizontalListBox(this ListBox source)
        {
            source.ItemsPanel = new ItemsPanelTemplate(new FrameworkElementFactory(typeof(WrapPanel)));
        }

        private static void setListViewItemPaddingToZero(ListBox source)
        {
            Style newStyle = new Style(typeof(ListBoxItem), (Style)source.Resources[typeof(ListBoxItem)]);
            Setter setter = new Setter();
            setter.Property = ListBoxItem.PaddingProperty;
            setter.Value = new Thickness(0);
            newStyle.Setters.Add(setter);
            source.Resources[typeof(ListBoxItem)] = newStyle;
        }

        #endregion

        #region Store Settings Specific To ListBox

        public static ListBox SetMaxRows(this ListBox chain, int maxRows)
        {
            chain.StoreSetting<ListBox>("MaxRows", maxRows);
            return chain;
        }

      
        public static ListBox SetScrollViewerPadding(this ListBox chain, Thickness padding)
        {
            chain.StoreSetting<ListBox>("ScrollViewerPadding", padding);
            return chain;
        }

        public static ListBox SetBackgroundColor(this ListBox chain, Brush brush)
        {
            Brush newBrush = (brush);
            chain.Background = newBrush;
            return chain;
        }

        public static ListBox SetBackgroundColorGradient(this ListBox chain, string gradientStop1Color, double gradientStop1Offset, string gradientStop2Color, double gradientStop2Offset, int startPointX, int startPointY, int endPointX, int endPointY)
        {
            LinearGradientBrush backgroundLinearBrush = BrushExt.CreateGradientBrush(gradientStop1Color, gradientStop1Offset, gradientStop2Color, gradientStop2Offset, startPointX, startPointY, endPointX, endPointY);

            chain.Background = backgroundLinearBrush;
            return chain;
        }

        public static ListBox SetBackgroundColor(this ListBox chain, byte red, byte green, byte blue, byte alpha)
        {
            Brush newBrush = (Brush)(new
System.Windows.Media.SolidColorBrush(new System.Windows.Media.Color() { R = red, G = green, B = blue, A = alpha }));
            chain.Background = newBrush;
            return chain;
        }

        public static SettingsChain<ListBox> SetSelectedContentControlColor(this SettingsChain<ListBox> chain, Color color)
        {
            ((ListBox)chain.Item1).SetSelectedContentControlColor(color);

            return chain;
        }

        public static ListBox SetSelectedContentControlColor(this ListBox chain, Color color)
        {
            chain.Resources.Add(SystemColors.HighlightBrushKey, new SolidColorBrush(color));
            chain.Resources.Add(SystemColors.ControlBrushKey, new SolidColorBrush(color));
            return chain;
        }

        public static ListBox SetSelectedContentControlForeground(this ListBox chain, Brush color)
        {
            chain.Resources.Add(SystemColors.HighlightTextBrushKey, color);
            return chain;
        }

        public static ListBox SetSelectedContentControlColorGradient(this
ListBox chain, string gradientStop1Color, double gradientStop1Offset, string gradientStop2Color, double gradientStop2Offset, int startPointX, int startPointY, int endPointX, int endPointY)
        {
            GradientStopCollection gradientStopsLinearBrush = new GradientStopCollection();
            gradientStopsLinearBrush.Add(
                new GradientStop((Color)ColorConverter.ConvertFromString(gradientStop1Color), gradientStop1Offset));
            gradientStopsLinearBrush.Add(
                new GradientStop((Color)ColorConverter.ConvertFromString(gradientStop2Color), gradientStop2Offset));

            chain.Resources.Add(SystemColors.HighlightBrushKey, new LinearGradientBrush(gradientStopsLinearBrush)
            {
                StartPoint = new Point(startPointX, startPointY),
                EndPoint = new Point(endPointX, endPointY)
            });
            chain.Resources.Add(SystemColors.ControlBrushKey, new LinearGradientBrush(gradientStopsLinearBrush)
            {
                StartPoint = new Point(startPointX, startPointY),
                EndPoint = new Point(endPointX, endPointY)
            });

            return chain;
        }

        #endregion
       
        #region Selected Key, Border, and Content Control, and ScrollIntoView

        //Implement an interface for Selected, similar to ScrollViewer?!?

        public static string SelectedKey(this System.Windows.Controls.SelectionChangedEventArgs selectedArgs)
        {
            String retVal = String.Empty;
            if (selectedArgs.AddedItems.Count > 0)
            {
                if (selectedArgs.AddedItems[0].Cast<Border>().Child.Cast<ContentControl>().Tag is ListBox)
                {
                    retVal = selectedArgs.AddedItems[0].Cast<Border>().Child.Cast<ContentControl>().Tag.Cast<ListBox>().SelectedKey();
                }

                if (selectedArgs.AddedItems[0].Cast<Border>().Child.Cast<ContentControl>().Tag is ListView)
                {
                    retVal = selectedArgs.AddedItems[0].Cast<Border>().Child.Cast<ContentControl>().Tag.Cast<ListView>().SelectedKey();
                }
            }

            return retVal;
        }

        public static void SetSelectedKey(this ListBox source, string key)
        {
            System.Collections.Generic.KeyValuePair<string, object> pair = (System.Collections.Generic.KeyValuePair<string, object>)source.GetCompositeLookupDictionary().Where<System.Collections.Generic.KeyValuePair<string, object>>((kvp, i) =>
            {
                if (kvp.Key == key)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }).First();

            if (pair.Value != null)
            {
                source.SelectedItem = (Border)pair.Value;
                source.ScrollIntoView(source.SelectedItem);
            }
        }

        public static void ScrollIntoViewByKey(this ListBox source, string key)
        {
            System.Collections.Generic.KeyValuePair<string, object> pair = (System.Collections.Generic.KeyValuePair<string, object>)source.GetCompositeLookupDictionary().Where<System.Collections.Generic.KeyValuePair<string, object>>((kvp, i) =>
            {
                if (kvp.Key == key)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }).First();

            if (pair.Value != null)
            {
                source.ScrollIntoView((Border)pair.Value);
            }
        }

        public static string SelectedKey(this ListBox source)
        {
            string retVal = null;

            Dictionary<string, object> CompositeLookup = CommonExt.GetCompositeLookupDictionaryFromComposite<FrameworkElement>(source.SelectedBorder());

            System.Collections.Generic.KeyValuePair<string, object> ibe = CompositeLookup.Where(row =>
            {
                if (row.Value == source.SelectedBorder()) { return true; } else { return false; }
            }).First();

            retVal = ibe.Key.ToString();

            return retVal;
        }

        public static bool StillHasItemSelected(this System.Windows.Controls.SelectionChangedEventArgs eav)
        {
            bool retVal = false;
            if (eav.AddedItems.Count > 0)
            {
                retVal = true;
            }

            return retVal;
        }

        public static Border SelectedBorder(this ListBox source)
        {
            Border returnedObject = null;

            object x = source.SelectedItem;

            if (x != null)
            {
                returnedObject = ((Border)x);
            }

            return returnedObject;
        }

        public static Border SelectedBorder(this System.Windows.Controls.SelectionChangedEventArgs selectedArgs)
        {
            return selectedArgs.AddedItems[0].Cast<Border>();
        }

        public static ContentControl SelectedContentControl(this System.Windows.Controls.SelectionChangedEventArgs selectedArgs)
        {
            return selectedArgs.AddedItems[0].Cast<Border>().Child.Cast<ContentControl>();
        }

        public static ContentControl SelectedContentControl(this ListBox source)
        {
            ContentControl returnedObject = null;

            object x = source.SelectedItem;

            if (x != null)
            {

                if (x is Border)
                {
                    returnedObject = ((Border)x).Child.Cast<ContentControl>();
                }
                else
                {
                    returnedObject = x.Cast<ContentControl>();
                }
            }

            return returnedObject;
        }

        #endregion

    }//end of class
}//end of namespace
